﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;
using System.Reflection;

namespace ConsoleApplication
{
    struct AuthInfo
    {
        public string username;
        public string password;
    }

    internal class XMLConfig
    {
        public AuthInfo DropBoxAccountInfo;
        public List<MovieFeed> FeedUrls;
        public Dictionary<string, string> Regexes;

        public XMLConfig()
        {
            FeedUrls = new List<MovieFeed>();
            Regexes = new Dictionary<string, string>();
            DropBoxAccountInfo = new AuthInfo();
        }

        public override string ToString()
        {
            XmlDocument doc = new XmlDocument();
            using (MemoryStream ms = new MemoryStream())
            {
                XmlTextWriter write = new XmlTextWriter(ms, Encoding.UTF8);

                write.Formatting = Formatting.Indented;

                write.WriteStartDocument();
                write.WriteStartElement("config");

                write.WriteStartElement("dropbox");
                write.WriteElementString("username", DropBoxAccountInfo.username);
                write.WriteElementString("password", DropBoxAccountInfo.password);
                write.WriteEndElement();

                write.WriteStartElement("feedurls");
                foreach (MovieFeed feed in FeedUrls)
                {
                    write.WriteStartElement("feed");
                    write.WriteElementString("type", feed.GetType().ToString());
                    write.WriteElementString("url", feed.FeedUri.ToString());
                    write.WriteEndElement();
                }
                write.WriteEndElement();

                write.WriteStartElement("regexes");
                foreach (KeyValuePair<string,string> kvp in Regexes)
                {
                    write.WriteStartElement("regex");
                    write.WriteElementString("name", kvp.Key);
                    write.WriteElementString("pattern", kvp.Value);
                    write.WriteEndElement();
                }
                write.WriteEndElement();

                write.WriteEndElement();
                write.WriteEndDocument();

                write.Formatting = Formatting.Indented;

                write.Close();

                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        public string ToEncryptedString(string password)
        {
            XmlDocument doc = new XmlDocument();
            using (MemoryStream ms = new MemoryStream())
            {
                XmlTextWriter write = new XmlTextWriter(ms, Encoding.UTF8);

                write.Formatting = Formatting.Indented;

                write.WriteStartDocument();
                write.WriteStartElement("config");

                write.WriteStartElement("dropbox");
                write.WriteElementString("username", Encrypt(password, DropBoxAccountInfo.username, "username"));
                write.WriteElementString("password", Encrypt(password, DropBoxAccountInfo.password, "password"));
                write.WriteEndElement();

                write.WriteStartElement("feedurls");
                foreach (MovieFeed feed in FeedUrls)
                {
                    write.WriteStartElement("feed");
                    write.WriteElementString("type", feed.GetType().ToString());
                    if (feed is EncryptedMovieFeed)
                    {
                        write.WriteElementString("url", Encrypt(password, feed.FeedUri.ToString(), "url"));
                    }
                    else
                    {
                        write.WriteElementString("url", feed.FeedUri.ToString());   
                    }
                    write.WriteEndElement();
                }
                write.WriteEndElement();

                write.WriteStartElement("regexes");
                foreach (KeyValuePair<string, string> kvp in Regexes)
                {
                    write.WriteStartElement("regex");
                    write.WriteElementString("name", kvp.Key);
                    write.WriteElementString("pattern", kvp.Value);
                    write.WriteEndElement();
                }
                write.WriteEndElement();

                write.WriteEndElement();
                write.WriteEndDocument();

                write.Close();

                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        public static XMLConfig FromString(string str, string password)
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml(str.Substring(str.IndexOf("<")));

            XMLConfig config = new XMLConfig();

            config.DropBoxAccountInfo.username = Decrypt(password, document.SelectSingleNode("/config/dropbox/username").InnerText, "username");
            config.DropBoxAccountInfo.password = Decrypt(password, document.SelectSingleNode("/config/dropbox/password").InnerText, "password");

            config.FeedUrls = new List<MovieFeed>();

            foreach (XmlNode feed in document.SelectSingleNode("/config/feedurls").ChildNodes)
            {
                try
                {
                    string type = feed["type"].InnerText;
                    string url = feed["url"].InnerText;

                    Type t = Assembly.GetExecutingAssembly().GetType(type);
                    if (t.IsSubclassOf(typeof(EncryptedMovieFeed)))
                        url = Decrypt(password, url, "url");
                    MovieFeed feedInstance = (MovieFeed)Activator.CreateInstance(t, url);

                    config.FeedUrls.Add(feedInstance);
                }
                catch (Exception e)
                {
                    Console.WriteLine("config crash!! " + e.ToString());
                }
            }

            config.Regexes = new Dictionary<string,string>();

            foreach (XmlNode regex in document.SelectSingleNode("/config/regexes").ChildNodes)
            {
                try
                {
                    config.Regexes[regex["name"].InnerText] = regex["pattern"].InnerText;
                }
                catch (Exception e)
                {
                    Console.WriteLine("config crash! " + e.ToString());
                }
            }

            return config;
        }

        public static string Decrypt(string password, string str, string salt)
        {
            return StringEncryption.Decrypt(str, password, salt, "SHA1", 2, "@1B2c3D4e5F6g7H8", 256);
        }

        public static string Encrypt(string password, string str, string salt)
        {
            return StringEncryption.Encrypt(str, password, salt, "SHA1", 2, "@1B2c3D4e5F6g7H8", 256);
        }
    }
}
